package com.rz.mvcframework.servlet;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.rz.mvcframework.annotation.RZAutowired;
import com.rz.mvcframework.annotation.RZController;
import com.rz.mvcframework.annotation.RZRequestMapping;
import com.rz.mvcframework.annotation.RZService;

/**
 * 核心前置控制器
 * @author 18217
 *
 */
public class RzDispatcherServlet extends HttpServlet {

	private static final long serialVersionUID = -3660566549561505828L;

	private Properties contextConfig = new Properties();

	private List<String> classNames = new ArrayList<String>();

	private Map<String, Object> ioc = new HashMap<String, Object>();
	
	private Map<String,Method> handlerMapping = new HashMap<String, Method>();

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		this.doPost(req, resp);
	}

	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
            
		try {
			doDispatch(req,resp);
		} catch (Exception e) {
			e.printStackTrace();
			resp.getWriter().write("500 Exception:"+Arrays.toString(e.getStackTrace()));
		}
	}

	
	private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
       
		if(this.handlerMapping.isEmpty()) {return;}
		
		String uri = req.getRequestURI();//绝对路径
		String contextPath = req.getContextPath();
		uri = uri.replace(contextPath, "").replaceAll("/+", "/");
		
		if(!this.handlerMapping.containsKey(uri)) {	
			resp.getWriter().write("404 Not Found!");
			return;
		}
		
		//通过反射机制去动态调用该方法
		Method method = this.handlerMapping.get(uri);
		Map<String,Object[]> params = req.getParameterMap();
		
		//获取形参列表
		Class<?>[] paramTypes = method.getParameterTypes();
		
		//保持存实参列表
		Object[] paramValues = new Object[paramTypes.length];
		for (int i = 0; i < paramTypes.length; i++) {
			Class paramType = paramTypes[i];
			if(paramType == HttpServletRequest.class) {
				paramValues[i] = req;
			}else if(paramType == HttpServletResponse.class) {
				paramValues[i] = resp;
			}else if(paramType == String.class) {
				for (Entry<String, Object[]> param : params.entrySet()) {
					String value = Arrays.toString(param.getValue())
							       .replaceAll("\\[|\\]", "")
							       .replaceAll(",\\s", ",");
					paramValues[i] = value;
				}
			}
		}
		
		String beanName = firstLowerCase(method.getDeclaringClass().getSimpleName());
		method.invoke(ioc.get(beanName),paramValues);
		
		System.out.println(method);
	}

	@Override
	public void init(ServletConfig config) throws ServletException {
		super.init(config);

		// 1. 加载配置文件
		doLoadConfig(config.getInitParameter("contextConfigLocation"));

		// 2. 扫描所有相关联的类
		doScanner(contextConfig.getProperty("scanPackage"));

		// 3. 将扫描出来的类进行实例化，并且存到IOC容器之中
		doInstance();

		// 4. 完成属性自动赋值，DI操作
		doAutowired();

		// 5. 初始化HandlerMapping
		initHandlerMapping();
		
		System.out.println("RZ Spring init done.");

	}

	private void doScanner(String scanPackage) {

		URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
		File classDir = new File(url.getFile());
		for (File file : classDir.listFiles()) {
			if (file.isDirectory()) {
				doScanner(scanPackage + "." + file.getName());
			} else {
				String className = (scanPackage + "." + file.getName().replace(".class", ""));
				classNames.add(className);
			}

		}
	}

	private void doLoadConfig(String contextConfigLocation) {

		InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
		try {
			contextConfig.load(is);
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (null != is) {
				try {
					is.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	private void initHandlerMapping() {
		
		if(ioc.isEmpty()) {return;}
		
		for(Map.Entry<String,Object> entry : ioc.entrySet()) {
			
			Class<?> clazz = entry.getValue().getClass();
			if(!clazz.isAnnotationPresent(RZController.class)) {continue;}
			
			String baseUrl = "";
			if(clazz.isAnnotationPresent(RZRequestMapping.class)) {
				
				RZRequestMapping requestMapping = clazz.getAnnotation(RZRequestMapping.class);
				baseUrl = requestMapping.value();
			}
			
			Method[] methods = clazz.getMethods();
			for (Method method : methods) {
				if(method.isAnnotationPresent(RZRequestMapping.class)) {
					
					RZRequestMapping requestMapping = method.getAnnotation(RZRequestMapping.class);
					String url = (baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
					handlerMapping.put(url, method);
					System.out.println("Mapped:" + url + "," + method);
				}
			}
 			
		}

	}

	private void doAutowired() {

		 if (ioc.isEmpty()) {return;}
		 
		 for (Map.Entry<String, Object> entry : ioc.entrySet()) {
			  Field[] fields = entry.getValue().getClass().getDeclaredFields();
			  for (Field field : fields) {
				   if(!field.isAnnotationPresent(RZAutowired.class)) { continue; }
				   
				   RZAutowired autowired = field.getAnnotation(RZAutowired.class);
				   String beanName = autowired.value().trim();
				   if("".equals(beanName)) {
					   beanName = field.getType().getName();
				   }
				   
				   //强制反射访问所有字段(包含private)
				   field.setAccessible(true);
					  
					try {
						 //用代码，用反射给字段赋值
						field.set(entry.getValue(), ioc.get(beanName));
					} catch (IllegalArgumentException e) {
						e.printStackTrace();
					} catch (IllegalAccessException e) {
						e.printStackTrace();
					}
				  
			  }
		 }
			
		
	}

	private void doInstance() {

		if (classNames.isEmpty()) { return; }
		try {

			for (String className : classNames) {

				Class<?> clazz = Class.forName(className);

				if (clazz.isAnnotationPresent(RZController.class)) {

					String beanName = firstLowerCase(clazz.getSimpleName());
					Object instance = clazz.newInstance();
					ioc.put(beanName, instance);

				} else if (clazz.isAnnotationPresent(RZService.class)) {

					/**
					 * 1、 默认首字母小写 2、如果自定义别名，优先采用别名
					 */
					RZService rzService = clazz.getAnnotation(RZService.class);
					String beanName = rzService.value();
					if ("".equals(beanName)) {
						beanName = firstLowerCase(clazz.getSimpleName());
					}
					Object instance = clazz.newInstance();
					ioc.put(beanName, instance);

					// 3、采用接口的全名作为key，实现的实例作为值
					Class<?>[] classes = clazz.getInterfaces();
					for (Class<?> class1 : classes) {
						if (ioc.containsKey(class1.getName())) {
							throw new Exception("already exists!");
						}
						ioc.put(class1.getName(), instance);
					}

				} else {
					// 否则直接忽略
					continue;
				}
			}
		} catch (Exception e) {

			e.printStackTrace();
		}

	}

	/**
	 * 首字母小写
	 * 
	 * @param simpleName
	 * @return
	 */
	private String firstLowerCase(String simpleName) {

		char[] chars = simpleName.toCharArray();
		chars[0] += 32;
		return String.valueOf(chars);

	}

}
